#region Copyright (c) 2006-2011 Widgetsphere LLC, All Rights Reserved
//--------------------------------------------------------------------- *
//                          Widgetsphere  LLC                           *
//             Copyright (c) 2006-2011 All Rights reserved              *
//                                                                      *
//                                                                      *
//This file and its contents are protected by United States and         *
//International copyright laws.  Unauthorized reproduction and/or       *
//distribution of all or any portion of the code contained herein       *
//is strictly prohibited and will result in severe civil and criminal   *
//penalties.  Any violations of this copyright will be prosecuted       *
//to the fullest extent possible under law.                             *
//                                                                      *
//THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
//TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
//TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
//CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
//THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF WIDGETSPHERE LLC   *
//                                                                      *
//UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
//PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME, OR  *
//SUBSTANTIALLY THE SAME, FUNCTIONALITY AS ANY WIDGETSPHERE PRODUCT.    *
//                                                                      *
//THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
//CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF WIDGETSPHERE,      *
//INC.  THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO       *
//INSURE ITS CONFIDENTIALITY.                                           *
//                                                                      *
//THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
//PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
//EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
//THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
//SOURCE CODE CONTAINED HEREIN.                                         *
//                                                                      *
//THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
//--------------------------------------------------------------------- *
#endregion
using System;
using System.Linq;
using Widgetsphere.Generator.Common.GeneratorFramework;
using Widgetsphere.Generator.Models;
using System.Text;
using Widgetsphere.Generator.Common.Util;
using System.Collections.Generic;
using Widgetsphere.Generator.Common;

namespace Widgetsphere.Generator.EFSelfTrackingServer.Generators.Contexts
{
	public class ContextGeneratedTemplate : EFSelfTrackingServerBaseTemplate
	{
		private StringBuilder sb = new StringBuilder();

		public ContextGeneratedTemplate(ModelRoot model)
		{
			_model = model;
		}

		#region BaseClassTemplate overrides
		public override string FileName
		{
			get { return _model.ProjectName + "Entities.Generated.cs"; }
		}

		public string ParentItemName
		{
			get { return _model.ProjectName + "Entities.cs"; }
		}

		public override string FileContent
		{
			get
			{
				GenerateContent();
				return sb.ToString();
			}
		}
		#endregion

		#region GenerateContent

		private void GenerateContent()
		{
			try
			{
				Widgetsphere.Generator.GenerationHelper.AppendCopyrightInCode(sb, _model);
				this.AppendUsingStatements();
				sb.AppendLine("namespace " + this.GetLocalNamespace());
				sb.AppendLine("{");
				this.AppendClass();
				this.AppendExtensions();
				sb.AppendLine("}");
				sb.AppendLine();
			}
			catch (Exception ex)
			{
				throw;
			}
		}

		private void AppendUsingStatements()
		{
			sb.AppendLine("using System;");
			sb.AppendLine("using System.Collections.Generic;");
			sb.AppendLine("using System.ComponentModel;");
			sb.AppendLine("using System.Data.Common;");
			sb.AppendLine("using System.Data.EntityClient;");
			sb.AppendLine("using System.Data.Metadata.Edm;");
			sb.AppendLine("using System.Data.Objects.DataClasses;");
			sb.AppendLine("using System.Data.Objects;");
			sb.AppendLine("using System.Data;");
			sb.AppendLine("using System.Diagnostics;");
			sb.AppendLine("using System.Globalization;");
			sb.AppendLine("using System.Linq;");
			sb.AppendLine("using " + this.GetClientNamespace() + ";");
			sb.AppendLine();
		}

		private void AppendClass()
		{
			sb.AppendLine("	public partial class " + _model.ProjectName + "Entities : ObjectContext");
			sb.AppendLine("	{");
			sb.AppendLine("		public const string ConnectionString = \"name=" + _model.ProjectName + "Entities\";");
			sb.AppendLine("		public const string ContainerName = \"" + _model.ProjectName + "Entities\";");
			sb.AppendLine();
			sb.AppendLine("		#region Constructors");
			sb.AppendLine();
			sb.AppendLine("		public " + _model.ProjectName + "Entities()");
			sb.AppendLine("			: base(ConnectionString, ContainerName)");
			sb.AppendLine("		{");
			sb.AppendLine("			Initialize();");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		public " + _model.ProjectName + "Entities(string connectionString)");
			sb.AppendLine("			: base(connectionString, ContainerName)");
			sb.AppendLine("		{");
			sb.AppendLine("			Initialize();");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		public " + _model.ProjectName + "Entities(EntityConnection connection)");
			sb.AppendLine("			: base(connection, ContainerName)");
			sb.AppendLine("		{");
			sb.AppendLine("			Initialize();");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private void Initialize()");
			sb.AppendLine("		{");
			sb.AppendLine("			// Creating proxies requires the use of the ProxyDataContractResolver and");
			sb.AppendLine("			// may allow lazy loading which can expand the loaded graph during serialization.");
			sb.AppendLine("			ContextOptions.ProxyCreationEnabled = false;");
			sb.AppendLine("			ObjectMaterialized += new ObjectMaterializedEventHandler(HandleObjectMaterialized);");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private void HandleObjectMaterialized(object sender, ObjectMaterializedEventArgs e)");
			sb.AppendLine("		{");
			sb.AppendLine("			var entity = e.Entity as " + this.GetClientNamespace() + ".IObjectWithChangeTracker;");
			sb.AppendLine("			if (entity != null)");
			sb.AppendLine("			{");
			sb.AppendLine("				bool changeTrackingEnabled = entity.ChangeTracker.ChangeTrackingEnabled;");
			sb.AppendLine("				try");
			sb.AppendLine("				{");
			sb.AppendLine("					entity.MarkAsUnchanged();");
			sb.AppendLine("				}");
			sb.AppendLine("				finally");
			sb.AppendLine("				{");
			sb.AppendLine("					entity.ChangeTracker.ChangeTrackingEnabled = changeTrackingEnabled;");
			sb.AppendLine("				}");
			sb.AppendLine("				this.StoreReferenceKeyValues(entity);");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		#endregion");
			sb.AppendLine();
			sb.AppendLine("		#region ObjectSet Properties");
			sb.AppendLine();

			foreach (var table in _model.Database.Tables.Where(x => x.Generated && !x.AssociativeTable).OrderBy(x => x.Name))
			{
				sb.AppendLine("		/// <summary>");
				sb.AppendLine("		/// The context object for the " + table.PascalName + " entity");
				sb.AppendLine("		/// </summary>");
				sb.AppendLine("		public virtual ObjectSet<" + this.GetClientNamespace() + "." + table.PascalName + "> " + table.PascalName);
				sb.AppendLine("		{");
				sb.AppendLine("			get { return _" + table.CamelName + " ?? (_" + table.CamelName + " = CreateObjectSet<" + this.GetClientNamespace() + "." + table.PascalName + ">(\"" + table.PascalName + "\")); }");
				sb.AppendLine("		}");
				sb.AppendLine("		private ObjectSet<" + this.GetClientNamespace() + "." + table.PascalName + "> _" + table.CamelName + ";");
				sb.AppendLine();
			}

			sb.AppendLine("		#endregion");
			sb.AppendLine("	}");
			sb.AppendLine();
		}

		private void AppendExtensions()
		{
			sb.AppendLine("	public static class SelfTrackingEntitiesContextExtensions");
			sb.AppendLine("	{");
			sb.AppendLine("		/// <summary>");
			sb.AppendLine("		/// ApplyChanges takes the changes in a connected set of entities and applies them to an ObjectContext.");
			sb.AppendLine("		/// </summary>");
			sb.AppendLine("		/// <typeparam name=\"TEntity\">Expected type of the ObjectSet</typeparam>");
			sb.AppendLine("		/// <param name=\"objectSet\">The ObjectSet referencing the ObjectContext to which changes will be applied.</param>");
			sb.AppendLine("		/// <param name=\"entity\">The entity serving as the entry point of the object graph that contains changes.</param>");
			sb.AppendLine("		public static void ApplyChanges<TEntity>(this ObjectSet<TEntity> objectSet, TEntity entity) where TEntity : class, " + this.GetClientNamespace() + ".IObjectWithChangeTracker");
			sb.AppendLine("		{");
			sb.AppendLine("			if (objectSet == null)");
			sb.AppendLine("			{");
			sb.AppendLine("				throw new ArgumentNullException(\"objectSet\");");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			objectSet.Context.ApplyChanges<TEntity>(objectSet.EntitySet.EntityContainer.Name + \".\" + objectSet.EntitySet.Name, entity);");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		/// <summary>");
			sb.AppendLine("		/// ApplyChanges takes the changes in a connected set of entities and applies them to an ObjectContext.");
			sb.AppendLine("		/// </summary>");
			sb.AppendLine("		/// <typeparam name=\"TEntity\">Expected type of the EntitySet</typeparam>");
			sb.AppendLine("		/// <param name=\"context\">The ObjectContext to which changes will be applied.</param>");
			sb.AppendLine("		/// <param name=\"entitySetName\">The EntitySet name of the entity.</param>");
			sb.AppendLine("		/// <param name=\"entity\">The entity serving as the entry point of the object graph that contains changes.</param>");
			sb.AppendLine("		public static void ApplyChanges<TEntity>(this ObjectContext context, string entitySetName, TEntity entity) where TEntity : " + this.GetClientNamespace() + ".IObjectWithChangeTracker");
			sb.AppendLine("		{");
			sb.AppendLine("			if (context == null)");
			sb.AppendLine("			{");
			sb.AppendLine("				throw new ArgumentNullException(\"context\");");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			if (String.IsNullOrEmpty(entitySetName))");
			sb.AppendLine("			{");
			sb.AppendLine("				throw new ArgumentException(\"String parameter cannot be null or empty.\", \"entitySetName\");");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			if (entity == null)");
			sb.AppendLine("			{");
			sb.AppendLine("				throw new ArgumentNullException(\"entity\");");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			bool lazyLoadingSetting = context.ContextOptions.LazyLoadingEnabled;");
			sb.AppendLine("			try");
			sb.AppendLine("			{");
			sb.AppendLine("				context.ContextOptions.LazyLoadingEnabled = false;");
			sb.AppendLine();
			sb.AppendLine("				EntityIndex entityIndex = AddHelper.AddAllEntities(context, entitySetName, entity);");
			sb.AppendLine("				RelationshipSet allRelationships = new RelationshipSet(context, entityIndex.AllEntities);");
			sb.AppendLine();
			sb.AppendLine("				#region Handle Initial Entity State");
			sb.AppendLine();
			sb.AppendLine("				foreach (" + this.GetClientNamespace() + ".IObjectWithChangeTracker changedEntity in entityIndex.AllEntities.Where(x => x.ChangeTracker.State == " + this.GetClientNamespace() + ".ObjectState.Deleted))");
			sb.AppendLine("				{");
			sb.AppendLine("					HandleDeletedEntity(context, entityIndex, allRelationships, changedEntity);");
			sb.AppendLine("				}");
			sb.AppendLine();
			sb.AppendLine("				foreach (" + this.GetClientNamespace() + ".IObjectWithChangeTracker changedEntity in entityIndex.AllEntities.Where(x => x.ChangeTracker.State != " + this.GetClientNamespace() + ".ObjectState.Deleted))");
			sb.AppendLine("				{");
			sb.AppendLine("					HandleEntity(context, entityIndex, allRelationships, changedEntity);");
			sb.AppendLine("				}");
			sb.AppendLine();
			sb.AppendLine("				#endregion");
			sb.AppendLine();
			sb.AppendLine("				#region Loop through each object state entries");
			sb.AppendLine();
			sb.AppendLine("				foreach (" + this.GetClientNamespace() + ".IObjectWithChangeTracker changedEntity in entityIndex.AllEntities)");
			sb.AppendLine("				{");
			sb.AppendLine("					ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(changedEntity);");
			sb.AppendLine("					EntityType entityType = context.MetadataWorkspace.GetCSpaceEntityType(changedEntity.GetType());");
			sb.AppendLine("					foreach (NavigationProperty navProp in entityType.NavigationProperties)");
			sb.AppendLine("					{");
			sb.AppendLine("						RelatedEnd relatedEnd = entry.GetRelatedEnd(navProp.Name);");
			sb.AppendLine("						if (!((AssociationType)relatedEnd.RelationshipSet.ElementType).IsForeignKey)");
			sb.AppendLine("						{");
			sb.AppendLine("							ApplyChangesToIndependentAssociation(context, (" + this.GetClientNamespace() + ".IObjectWithChangeTracker)changedEntity, entry, navProp, relatedEnd, allRelationships);");
			sb.AppendLine("						}");
			sb.AppendLine();
			sb.AppendLine("					}");
			sb.AppendLine("				}");
			sb.AppendLine("				#endregion");
			sb.AppendLine();
			sb.AppendLine("				// Change all the remaining relationships to the appropriate state");
			sb.AppendLine("				foreach (var relationship in allRelationships)");
			sb.AppendLine("				{");
			sb.AppendLine("					context.ObjectStateManager.ChangeRelationshipState(");
			sb.AppendLine("							relationship.End0,");
			sb.AppendLine("							relationship.End1,");
			sb.AppendLine("							relationship.AssociationSet.ElementType.FullName,");
			sb.AppendLine("							relationship.AssociationEndMembers[1].Name,");
			sb.AppendLine("							relationship.State);");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("			finally");
			sb.AppendLine("			{");
			sb.AppendLine("				context.ContextOptions.LazyLoadingEnabled = lazyLoadingSetting;");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static void ApplyChangesToIndependentAssociation(ObjectContext context, " + this.GetClientNamespace() + ".IObjectWithChangeTracker changedEntity, ObjectStateEntry entry, NavigationProperty navProp,");
			sb.AppendLine("				IRelatedEnd relatedEnd, RelationshipSet allRelationships)");
			sb.AppendLine("		{");
			sb.AppendLine("			" + this.GetClientNamespace() + ".ObjectChangeTracker changeTracker = changedEntity.ChangeTracker;");
			sb.AppendLine();
			sb.AppendLine("			if (changeTracker.State == " + this.GetClientNamespace() + ".ObjectState.Added)");
			sb.AppendLine("			{");
			sb.AppendLine("				// Relationships should remain added so remove them from the list of allRelationships");
			sb.AppendLine("				foreach (object relatedEntity in relatedEnd)");
			sb.AppendLine("				{");
			sb.AppendLine("					ObjectStateEntry addedRelationshipEntry =");
			sb.AppendLine("											context.ObjectStateManager.ChangeRelationshipState(");
			sb.AppendLine("													changedEntity,");
			sb.AppendLine("													relatedEntity,");
			sb.AppendLine("													navProp.Name,");
			sb.AppendLine("													EntityState.Added);");
			sb.AppendLine();
			sb.AppendLine("					allRelationships.Remove(addedRelationshipEntry);");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("			else");
			sb.AppendLine("			{");
			sb.AppendLine("				if (navProp.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)");
			sb.AppendLine("				{");
			sb.AppendLine("					//Handle removal to FixupCollections");
			sb.AppendLine("					" + this.GetClientNamespace() + ".ObjectList collectionPropertyChanges = null;");
			sb.AppendLine("					if (changeTracker.ObjectsRemovedFromCollectionProperties.TryGetValue(navProp.Name, out collectionPropertyChanges))");
			sb.AppendLine("					{");
			sb.AppendLine("						foreach (var removedEntityFromAssociation in collectionPropertyChanges)");
			sb.AppendLine("						{");
			sb.AppendLine("							ObjectStateEntry deletedRelationshipEntry =");
			sb.AppendLine("									context.ObjectStateManager.ChangeRelationshipState(");
			sb.AppendLine("											changedEntity,");
			sb.AppendLine("											removedEntityFromAssociation,");
			sb.AppendLine("											navProp.Name,");
			sb.AppendLine("											EntityState.Deleted);");
			sb.AppendLine();
			sb.AppendLine("							allRelationships.Remove(deletedRelationshipEntry);");
			sb.AppendLine("						}");
			sb.AppendLine("					}");
			sb.AppendLine();
			sb.AppendLine("					//Handle addition to FixupCollection");
			sb.AppendLine("					if (changeTracker.ObjectsAddedToCollectionProperties.TryGetValue(navProp.Name, out collectionPropertyChanges))");
			sb.AppendLine("					{");
			sb.AppendLine("						foreach (var addedEntityFromAssociation in collectionPropertyChanges)");
			sb.AppendLine("						{");
			sb.AppendLine("							ObjectStateEntry addedRelationshipEntry =");
			sb.AppendLine("									context.ObjectStateManager.ChangeRelationshipState(");
			sb.AppendLine("											changedEntity,");
			sb.AppendLine("											addedEntityFromAssociation,");
			sb.AppendLine("											navProp.Name,");
			sb.AppendLine("											EntityState.Added);");
			sb.AppendLine();
			sb.AppendLine("							allRelationships.Remove(addedRelationshipEntry);");
			sb.AppendLine("						}");
			sb.AppendLine("					}");
			sb.AppendLine("				}");
			sb.AppendLine("				else");
			sb.AppendLine("				{");
			sb.AppendLine();
			sb.AppendLine("					// Handle original relationship values");
			sb.AppendLine("					object originalReferenceValue;");
			sb.AppendLine("					if (changeTracker.OriginalValues.TryGetValue(navProp.Name, out originalReferenceValue))");
			sb.AppendLine("					{");
			sb.AppendLine("						if (originalReferenceValue != null)");
			sb.AppendLine("						{");
			sb.AppendLine("							//Capture the deletion of association");
			sb.AppendLine("							ObjectStateEntry deletedRelationshipEntry =");
			sb.AppendLine("									context.ObjectStateManager.ChangeRelationshipState(");
			sb.AppendLine("											entry.Entity,");
			sb.AppendLine("											originalReferenceValue,");
			sb.AppendLine("											navProp.Name,");
			sb.AppendLine("											EntityState.Deleted);");
			sb.AppendLine();
			sb.AppendLine("							allRelationships.Remove(deletedRelationshipEntry);");
			sb.AppendLine("						}");
			sb.AppendLine();
			sb.AppendLine("						//Capture the Addition of association");
			sb.AppendLine("						object currentReferenceValue = null;");
			sb.AppendLine("						foreach (object o in relatedEnd)");
			sb.AppendLine("						{");
			sb.AppendLine("							currentReferenceValue = o;");
			sb.AppendLine("							break;");
			sb.AppendLine("						}");
			sb.AppendLine("						if (currentReferenceValue != null)");
			sb.AppendLine("						{");
			sb.AppendLine("							ObjectStateEntry addedRelationshipEntry =");
			sb.AppendLine("									context.ObjectStateManager.ChangeRelationshipState(");
			sb.AppendLine("											changedEntity,");
			sb.AppendLine("											currentReferenceValue,");
			sb.AppendLine("											navProp.Name,");
			sb.AppendLine("											EntityState.Added);");
			sb.AppendLine();
			sb.AppendLine("							allRelationships.Remove(addedRelationshipEntry);");
			sb.AppendLine("						}");
			sb.AppendLine("						// if the current value of the reference is null, then the user must set the entity reference to null");
			sb.AppendLine("						// which is already being handled by the deletion of the relationship");
			sb.AppendLine("					}");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		// Extracts the relationship key information from the ExtendedProperties and OriginalValues records of each ObjectChangeTracker");
			sb.AppendLine("		// This is done by:");
			sb.AppendLine("		//  1. Creating any existing relationship specified in the ExtendedProperties");
			sb.AppendLine("		//  2. Determine if there was a previous relationship, and if there was create a deleted relationship between the entity and the previous entity or key value");
			sb.AppendLine("		private static void HandleRelationshipKeys(ObjectContext context, EntityIndex entityIndex, RelationshipSet allRelationships, " + this.GetClientNamespace() + ".IObjectWithChangeTracker entity)");
			sb.AppendLine("		{");
			sb.AppendLine("			" + this.GetClientNamespace() + ".ObjectChangeTracker changeTracker = entity.ChangeTracker;");
			sb.AppendLine("			if (changeTracker.State == " + this.GetClientNamespace() + ".ObjectState.Unchanged ||");
			sb.AppendLine("					changeTracker.State == " + this.GetClientNamespace() + ".ObjectState.Modified ||");
			sb.AppendLine("					changeTracker.State == " + this.GetClientNamespace() + ".ObjectState.Deleted)");
			sb.AppendLine("			{");
			sb.AppendLine("				ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(entity);");
			sb.AppendLine("				EntityType entityType = context.MetadataWorkspace.GetCSpaceEntityType(entity.GetType());");
			sb.AppendLine("				RelationshipManager relationshipManager = context.ObjectStateManager.GetRelationshipManager(entity);");
			sb.AppendLine();
			sb.AppendLine("				foreach (var entityReference in EnumerateSaveReferences(relationshipManager))");
			sb.AppendLine("				{");
			sb.AppendLine("					AssociationSet associationSet = ((AssociationSet)entityReference.RelationshipSet);");
			sb.AppendLine("					AssociationEndMember fromEnd = associationSet.AssociationSetEnds[entityReference.SourceRoleName].CorrespondingAssociationEndMember;");
			sb.AppendLine("					AssociationEndMember toEnd = associationSet.AssociationSetEnds[entityReference.TargetRoleName].CorrespondingAssociationEndMember;");
			sb.AppendLine();
			sb.AppendLine("					// Find if there is a NavigationProperty for this candidate");
			sb.AppendLine("					NavigationProperty navigationProperty = entityType.NavigationProperties.");
			sb.AppendLine("																		 SingleOrDefault(x => x.RelationshipType == associationSet.ElementType &&");
			sb.AppendLine("																										 x.FromEndMember == fromEnd &&");
			sb.AppendLine("																										 x.ToEndMember == toEnd);");
			sb.AppendLine();
			sb.AppendLine("					// Only handle relationship keys in one of these cases");
			sb.AppendLine("					// 1. There is no navigation property");
			sb.AppendLine("					// 2. The navigation property has a null current reference value and there are no removes or adds");
			sb.AppendLine("					// 3. The navigation property has a current reference value, but there is no remove");
			sb.AppendLine();
			sb.AppendLine("					EntityKey currentKey = GetSavedReferenceKey(entityIndex, entityReference, entity, navigationProperty, changeTracker.ExtendedProperties);");
			sb.AppendLine();
			sb.AppendLine("					// Get any original value from the change tracking information");
			sb.AppendLine("					object originalValue = null;");
			sb.AppendLine("					EntityKey originalKey = null;");
			sb.AppendLine("					bool hasOriginalValue = false;");
			sb.AppendLine("					if (changeTracker.OriginalValues != null)");
			sb.AppendLine("					{");
			sb.AppendLine("						// Try to get the original value from the NavigationProperty first");
			sb.AppendLine("						if (navigationProperty != null)");
			sb.AppendLine("						{");
			sb.AppendLine("							hasOriginalValue = changeTracker.OriginalValues.TryGetValue(navigationProperty.Name, out originalValue);");
			sb.AppendLine("						}");
			sb.AppendLine("						// Try to get the original value from the reference key second");
			sb.AppendLine("						if (!hasOriginalValue || originalValue == null)");
			sb.AppendLine("						{");
			sb.AppendLine("							originalKey = GetSavedReferenceKey(entityIndex, entityReference, entity, navigationProperty, changeTracker.OriginalValues);");
			sb.AppendLine("						}");
			sb.AppendLine("					}");
			sb.AppendLine();
			sb.AppendLine("					// Create the current relationship");
			sb.AppendLine("					if (currentKey != null)");
			sb.AppendLine("					{");
			sb.AppendLine("						// If the key is for a deleted entity, move that key to an originalValue and fixup the entities key values");
			sb.AppendLine("						// Otherwise create a new relationship");
			sb.AppendLine("						ObjectStateEntry currentEntry;");
			sb.AppendLine("						if (context.ObjectStateManager.TryGetObjectStateEntry(currentKey, out currentEntry) &&");
			sb.AppendLine("							 currentEntry.Entity != null &&");
			sb.AppendLine("							 currentEntry.State == EntityState.Deleted)");
			sb.AppendLine("						{");
			sb.AppendLine("							entityReference.EntityKey = null;");
			sb.AppendLine("							MoveSavedReferenceKey(entityReference, entity, navigationProperty, changeTracker.ExtendedProperties, changeTracker.OriginalValues);");
			sb.AppendLine("							originalKey = currentKey;");
			sb.AppendLine("						}");
			sb.AppendLine("						else");
			sb.AppendLine("						{");
			sb.AppendLine("							CreateRelationship(context, entityReference, entry.EntityKey, currentKey, originalKey == null ? EntityState.Unchanged : EntityState.Added);");
			sb.AppendLine("						}");
			sb.AppendLine("					}");
			sb.AppendLine("					else");
			sb.AppendLine("					{");
			sb.AppendLine("						// Find the current key");
			sb.AppendLine("						// Cannot get the EntityKey directly because this is null when it points to an Added entity");
			sb.AppendLine("						currentKey = entityReference.GetCurrentEntityKey(context);");
			sb.AppendLine("					}");
			sb.AppendLine();
			sb.AppendLine("					// Create the original relationship");
			sb.AppendLine("					if (originalKey != null)");
			sb.AppendLine("					{");
			sb.AppendLine("						// If the key is for a deleted entity, remember to create a deleted relationship,");
			sb.AppendLine("						// otherwise use the entityReference to setup the deleted relationship");
			sb.AppendLine("						ObjectStateEntry originalEntry = null;");
			sb.AppendLine("						ObjectStateEntry deletedRelationshipEntry = null;");
			sb.AppendLine("						if (context.ObjectStateManager.TryGetObjectStateEntry(originalKey, out originalEntry) &&");
			sb.AppendLine("							 originalEntry.Entity != null &&");
			sb.AppendLine("							 originalEntry.State == EntityState.Deleted)");
			sb.AppendLine("						{");
			sb.AppendLine("							allRelationships.Add(entityReference, entry.Entity, originalEntry.Entity, EntityState.Deleted);");
			sb.AppendLine("						}");
			sb.AppendLine("						else");
			sb.AppendLine("						{");
			sb.AppendLine("							// To create a deleted relationship to a key, first detach the existing relationship between entry and currentKey");
			sb.AppendLine("							EntityState currentRelationshipState = DetachRelationship(context, entityReference, entry, currentKey);");
			sb.AppendLine();
			sb.AppendLine("							// If the relationship is 1 to 0..1, detach the relationship from currentKey to its target (targetKey)");
			sb.AppendLine("							EntityState targetRelationshipState = EntityState.Detached;");
			sb.AppendLine("							EntityReference targetReference = null;");
			sb.AppendLine("							EntityKey targetKey = null;");
			sb.AppendLine("							if (originalEntry != null &&");
			sb.AppendLine("									originalEntry.Entity != null &&");
			sb.AppendLine("									originalEntry.RelationshipManager != null &&");
			sb.AppendLine("									associationSet.AssociationSetEnds[fromEnd.Name].CorrespondingAssociationEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many)");
			sb.AppendLine("							{");
			sb.AppendLine("								targetReference = originalEntry.RelationshipManager.GetRelatedEnd(entityReference.RelationshipName, entityReference.SourceRoleName) as EntityReference;");
			sb.AppendLine("								targetKey = targetReference.GetCurrentEntityKey(context);");
			sb.AppendLine("								if (targetKey != null)");
			sb.AppendLine("								{");
			sb.AppendLine("									targetRelationshipState = DetachRelationship(context, targetReference, originalEntry, targetKey);");
			sb.AppendLine("								}");
			sb.AppendLine("							}");
			sb.AppendLine();
			sb.AppendLine();
			sb.AppendLine("							// Create the deleted relationship between entry and originalKey");
			sb.AppendLine("							deletedRelationshipEntry = CreateRelationship(context, entityReference, entry.EntityKey, originalKey, EntityState.Deleted);");
			sb.AppendLine();
			sb.AppendLine("							// Set the previous relationship between entry and currentKey back");
			sb.AppendLine("							CreateRelationship(context, entityReference, entry.EntityKey, currentKey, currentRelationshipState);");
			sb.AppendLine();
			sb.AppendLine("							// Set the previous relationship between originalEntry and targetKey back");
			sb.AppendLine("							if (targetKey != null)");
			sb.AppendLine("							{");
			sb.AppendLine("								CreateRelationship(context, targetReference, originalEntry.EntityKey, targetKey, targetRelationshipState);");
			sb.AppendLine("							}");
			sb.AppendLine("						}");
			sb.AppendLine("						if (deletedRelationshipEntry != null)");
			sb.AppendLine("						{");
			sb.AppendLine("							// Remove the deleted relationship from those that need to be processed later in ApplyChanges");
			sb.AppendLine("							allRelationships.Remove(deletedRelationshipEntry);");
			sb.AppendLine("						}");
			sb.AppendLine("					}");
			sb.AppendLine("					else if (currentKey == null && originalValue != null && entityReference.IsDependentEndOfReferentialConstraint())");
			sb.AppendLine("					{");
			sb.AppendLine("						// the graph won't have this hooked up because there is no current value, but there is an original value,");
			sb.AppendLine("						// so the relationship processing code will want to delete a relationship.");
			sb.AppendLine("						// we can add this one so it has a relationship to change to deleted.");
			sb.AppendLine("						context.ObjectStateManager.ChangeRelationshipState(");
			sb.AppendLine("																								entry.Entity,");
			sb.AppendLine("																								originalValue,");
			sb.AppendLine("																								entityReference.RelationshipName,");
			sb.AppendLine("																								entityReference.TargetRoleName,");
			sb.AppendLine("																								EntityState.Added);");
			sb.AppendLine("					}");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static ObjectStateEntry CreateRelationship(ObjectContext context, EntityReference entityReference, EntityKey fromKey, EntityKey toKey, EntityState state)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (state != EntityState.Detached)");
			sb.AppendLine("			{");
			sb.AppendLine("				AssociationSet associationSet = ((AssociationSet)entityReference.RelationshipSet);");
			sb.AppendLine("				AssociationEndMember fromEnd = associationSet.AssociationSetEnds[entityReference.SourceRoleName].CorrespondingAssociationEndMember;");
			sb.AppendLine("				AssociationEndMember toEnd = associationSet.AssociationSetEnds[entityReference.TargetRoleName].CorrespondingAssociationEndMember;");
			sb.AppendLine();
			sb.AppendLine("				// set the relationship to the original relationship in the unchanged state");
			sb.AppendLine("				Debug.Assert(toKey != null, \"why/how would we do a delete with a null originalKey?\");");
			sb.AppendLine();
			sb.AppendLine("				if (toKey.IsTemporary)");
			sb.AppendLine("				{");
			sb.AppendLine("					// Clear any existing relationship");
			sb.AppendLine("					entityReference.EntityKey = null;");
			sb.AppendLine();
			sb.AppendLine("					// If the target entity is Added, use Add on RelatedEnd");
			sb.AppendLine("					ObjectStateEntry targetEntry;");
			sb.AppendLine("					context.ObjectStateManager.TryGetObjectStateEntry(toKey, out targetEntry);");
			sb.AppendLine("					Debug.Assert(targetEntry != null, \"Should have found the state entry\");");
			sb.AppendLine("					((IRelatedEnd)entityReference).Add(targetEntry.Entity);");
			sb.AppendLine("				}");
			sb.AppendLine("				else");
			sb.AppendLine("				{");
			sb.AppendLine("					entityReference.EntityKey = toKey;");
			sb.AppendLine("				}");
			sb.AppendLine();
			sb.AppendLine("				ObjectStateEntry relationshipEntry;");
			sb.AppendLine("				bool found = context.TryGetObjectStateEntry(fromKey, toKey, associationSet, fromEnd, toEnd, out relationshipEntry);");
			sb.AppendLine("				Debug.Assert(found, \"Did not find the created relationship.\");");
			sb.AppendLine();
			sb.AppendLine("				switch (state)");
			sb.AppendLine("				{");
			sb.AppendLine("					case EntityState.Added:");
			sb.AppendLine("						break;");
			sb.AppendLine("					case EntityState.Unchanged:");
			sb.AppendLine("						relationshipEntry.AcceptChanges();");
			sb.AppendLine("						break;");
			sb.AppendLine("					case EntityState.Deleted:");
			sb.AppendLine("						relationshipEntry.AcceptChanges();");
			sb.AppendLine("						entityReference.EntityKey = null;");
			sb.AppendLine("						break;");
			sb.AppendLine("				}");
			sb.AppendLine("				return relationshipEntry;");
			sb.AppendLine("			}");
			sb.AppendLine("			return null;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static EntityState DetachRelationship(ObjectContext context, EntityReference entityReference, ObjectStateEntry fromEntry, EntityKey toKey)");
			sb.AppendLine("		{");
			sb.AppendLine("			EntityState currentRelationshipState = EntityState.Detached;");
			sb.AppendLine();
			sb.AppendLine("			if (toKey != null)");
			sb.AppendLine("			{");
			sb.AppendLine("				AssociationSet associationSet = ((AssociationSet)entityReference.RelationshipSet);");
			sb.AppendLine("				AssociationEndMember fromEnd = associationSet.AssociationSetEnds[entityReference.SourceRoleName].CorrespondingAssociationEndMember;");
			sb.AppendLine("				AssociationEndMember toEnd = associationSet.AssociationSetEnds[entityReference.TargetRoleName].CorrespondingAssociationEndMember;");
			sb.AppendLine();
			sb.AppendLine("				ObjectStateEntry currentRelationshipEntry = null;");
			sb.AppendLine();
			sb.AppendLine("				if (context.TryGetObjectStateEntry(fromEntry.EntityKey, toKey, associationSet, fromEnd, toEnd, out currentRelationshipEntry))");
			sb.AppendLine("				{");
			sb.AppendLine("					currentRelationshipState = currentRelationshipEntry.State;");
			sb.AppendLine();
			sb.AppendLine("					entityReference.EntityKey = null;");
			sb.AppendLine("					if (currentRelationshipEntry.State == EntityState.Deleted)");
			sb.AppendLine("					{");
			sb.AppendLine("						currentRelationshipEntry.AcceptChanges();");
			sb.AppendLine("					}");
			sb.AppendLine("					Debug.Assert(currentRelationshipEntry.State == EntityState.Detached, \"relationship was not detached\");");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("			return currentRelationshipState;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static string CreateReferenceKeyLookup(string keyMemberName, EntityReference reference, NavigationProperty navigationProperty)");
			sb.AppendLine("		{");
			sb.AppendLine("			// use the more usable navigation property name to qualify the member");
			sb.AppendLine("			// if available");
			sb.AppendLine("			if (navigationProperty != null)");
			sb.AppendLine("			{");
			sb.AppendLine("				return String.Format(CultureInfo.InvariantCulture, \"{0}.{1}\", navigationProperty.Name, keyMemberName);");
			sb.AppendLine("			}");
			sb.AppendLine("			else");
			sb.AppendLine("			{");
			sb.AppendLine("				return String.Format(CultureInfo.InvariantCulture, \"Navigate({0}.{1}).{2}\", reference.RelationshipSet.ElementType.FullName, reference.TargetRoleName, keyMemberName);");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		// retrieves the key corresponding to the passed in EntityReference");
			sb.AppendLine("		// these keys can be set during the ObjectMaterialized event or through relationship fixup");
			sb.AppendLine("		private static EntityKey GetSavedReferenceKey(EntityIndex entityIndex, EntityReference reference, object entity, NavigationProperty navigationProperty, IDictionary<string, object> values)");
			sb.AppendLine("		{");
			sb.AppendLine("			Debug.Assert(navigationProperty == null || reference.RelationshipSet.ElementType == navigationProperty.RelationshipType, \"the reference and navigationProperty should coorospond\");");
			sb.AppendLine();
			sb.AppendLine("			EntitySet entitySet = ((AssociationSet)reference.RelationshipSet).AssociationSetEnds[reference.TargetRoleName].EntitySet;");
			sb.AppendLine();
			sb.AppendLine("			List<EntityKeyMember> foundKeyMembers = new List<EntityKeyMember>(1);");
			sb.AppendLine("			bool foundNone = true;");
			sb.AppendLine("			bool missingSome = false;");
			sb.AppendLine("			foreach (var keyMember in entitySet.ElementType.KeyMembers)");
			sb.AppendLine("			{");
			sb.AppendLine("				string lookupKey = CreateReferenceKeyLookup(keyMember.Name, reference, navigationProperty);");
			sb.AppendLine("				object value;");
			sb.AppendLine("				if (values.TryGetValue(lookupKey, out value))");
			sb.AppendLine("				{");
			sb.AppendLine("					foundKeyMembers.Add(new EntityKeyMember(keyMember.Name, value));");
			sb.AppendLine("					foundNone = false;");
			sb.AppendLine("				}");
			sb.AppendLine("				else");
			sb.AppendLine("				{");
			sb.AppendLine("					missingSome = true;");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			if (foundNone)");
			sb.AppendLine("			{");
			sb.AppendLine("				// we didn't find a key");
			sb.AppendLine("				return null;");
			sb.AppendLine("			}");
			sb.AppendLine("			else if (missingSome)");
			sb.AppendLine("			{");
			sb.AppendLine("				throw new InvalidOperationException(");
			sb.AppendLine("						String.Format(");
			sb.AppendLine("								CultureInfo.CurrentCulture,");
			sb.AppendLine("								\"The OriginalValues or ExtendedProperties collections on the type '{0}' contained only a partial key to satisfy the relationship '{1}' targeting the role '{2}'\",");
			sb.AppendLine("								entity.GetType().FullName,");
			sb.AppendLine("								reference.RelationshipName,");
			sb.AppendLine("								reference.TargetRoleName));");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			EntityKey key = entityIndex.ConvertEntityKey(new EntityKey(reference.GetEntitySetName(), foundKeyMembers));");
			sb.AppendLine("			return key;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		// Moves the key corresponding to the passed in EntityReference from a source collection to a target collection");
			sb.AppendLine("		private static void MoveSavedReferenceKey(EntityReference reference, object entity, NavigationProperty navigationProperty, IDictionary<string, object> sourceValues, IDictionary<string, object> targetValues)");
			sb.AppendLine("		{");
			sb.AppendLine("			Debug.Assert(navigationProperty == null || reference.RelationshipSet.ElementType == navigationProperty.RelationshipType, \" the reference and navigationProperty should correspond\");");
			sb.AppendLine();
			sb.AppendLine("			EntitySet entitySet = ((AssociationSet)reference.RelationshipSet).AssociationSetEnds[reference.TargetRoleName].EntitySet;");
			sb.AppendLine();
			sb.AppendLine("			bool missingSome = false;");
			sb.AppendLine("			foreach (var keyMember in entitySet.ElementType.KeyMembers)");
			sb.AppendLine("			{");
			sb.AppendLine("				string lookupKey = CreateReferenceKeyLookup(keyMember.Name, reference, navigationProperty);");
			sb.AppendLine("				object value;");
			sb.AppendLine("				if (sourceValues.TryGetValue(lookupKey, out value))");
			sb.AppendLine("				{");
			sb.AppendLine("					if (targetValues.ContainsKey(lookupKey))");
			sb.AppendLine("					{");
			sb.AppendLine("						targetValues[lookupKey] = value;");
			sb.AppendLine("					}");
			sb.AppendLine("					else");
			sb.AppendLine("					{");
			sb.AppendLine("						targetValues.Add(lookupKey, value);");
			sb.AppendLine("					}");
			sb.AppendLine("					sourceValues.Remove(lookupKey);");
			sb.AppendLine("				}");
			sb.AppendLine("				else");
			sb.AppendLine("				{");
			sb.AppendLine("					missingSome = true;");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			if (missingSome)");
			sb.AppendLine("			{");
			sb.AppendLine("				throw new InvalidOperationException(");
			sb.AppendLine("						String.Format(");
			sb.AppendLine("								CultureInfo.CurrentCulture,");
			sb.AppendLine("								\" The OriginalValues or ExtendedProperties collections on the type '{0}' contained only a partial key to satisfy the relationship '{1}' targeting the role '{2}'\",");
			sb.AppendLine("								entity.GetType().FullName,");
			sb.AppendLine("								reference.RelationshipName,");
			sb.AppendLine("								reference.TargetRoleName));");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static IEnumerable<EntityReference> EnumerateSaveReferences(RelationshipManager manager)");
			sb.AppendLine("		{");
			sb.AppendLine("			return manager.GetAllRelatedEnds().OfType<EntityReference>()");
			sb.AppendLine("							.Where(er => er.RelationshipSet.ElementType.RelationshipEndMembers[er.SourceRoleName].RelationshipMultiplicity != RelationshipMultiplicity.One &&");
			sb.AppendLine("									!((AssociationSet)er.RelationshipSet).ElementType.IsForeignKey);");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		internal static void StoreReferenceKeyValues(this ObjectContext context, " + this.GetClientNamespace() + ".IObjectWithChangeTracker entity)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (entity == null)");
			sb.AppendLine("			{");
			sb.AppendLine("				throw new ArgumentNullException(\"entity\");");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			ObjectStateEntry entry;");
			sb.AppendLine("			if (!context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry))");
			sb.AppendLine("			{");
			sb.AppendLine("				// must be a no tracking query, the reference key info won't be available");
			sb.AppendLine("				return;");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			var relationshipManager = entry.RelationshipManager;");
			sb.AppendLine("			EntityType entityType = context.MetadataWorkspace.GetCSpaceEntityType(entity.GetType());");
			sb.AppendLine("			foreach (EntityReference entityReference in EnumerateSaveReferences(relationshipManager))");
			sb.AppendLine("			{");
			sb.AppendLine("				NavigationProperty navigationProperty = entityType.NavigationProperties.FirstOrDefault(n => n.RelationshipType == entityReference.RelationshipSet.ElementType &&");
			sb.AppendLine("								n.FromEndMember.Name == entityReference.SourceRoleName &&");
			sb.AppendLine("								n.ToEndMember.Name == entityReference.TargetRoleName);");
			sb.AppendLine();
			sb.AppendLine("				object value = entityReference.GetValue();");
			sb.AppendLine("				if ((navigationProperty == null || value == null) && entityReference.EntityKey != null)");
			sb.AppendLine("				{");
			sb.AppendLine("					foreach (var item in entityReference.EntityKey.EntityKeyValues)");
			sb.AppendLine("					{");
			sb.AppendLine("						string key = CreateReferenceKeyLookup(item.Key, entityReference, navigationProperty);");
			sb.AppendLine("						entity.ChangeTracker.ExtendedProperties.Add(key, item.Value);");
			sb.AppendLine("					}");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static void HandleEntity(ObjectContext context, EntityIndex entityIndex, RelationshipSet allRelationships, " + this.GetClientNamespace() + ".IObjectWithChangeTracker entity)");
			sb.AppendLine("		{");
			sb.AppendLine("			ChangeEntityStateBasedOnObjectState(context, entity);");
			sb.AppendLine("			HandleRelationshipKeys(context, entityIndex, allRelationships, entity);");
			sb.AppendLine("			UpdateOriginalValues(context, entity);");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static void HandleDeletedEntity(ObjectContext context, EntityIndex entityIndex, RelationshipSet allRelationships, " + this.GetClientNamespace() + ".IObjectWithChangeTracker entity)");
			sb.AppendLine("		{");
			sb.AppendLine("			HandleRelationshipKeys(context, entityIndex, allRelationships, entity);");
			sb.AppendLine("			ChangeEntityStateBasedOnObjectState(context, entity);");
			sb.AppendLine("			UpdateOriginalValues(context, entity);");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static void UpdateOriginalValues(ObjectContext context, " + this.GetClientNamespace() + ".IObjectWithChangeTracker entity)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (entity.ChangeTracker.State == " + this.GetClientNamespace() + ".ObjectState.Unchanged ||");
			sb.AppendLine("					entity.ChangeTracker.State == " + this.GetClientNamespace() + ".ObjectState.Added ||");
			sb.AppendLine("					entity.ChangeTracker.OriginalValues == null)");
			sb.AppendLine("			{");
			sb.AppendLine("				// nothing to do here");
			sb.AppendLine("				return;");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			// we only need/want to deal with scalar and complex properties");
			sb.AppendLine();
			sb.AppendLine("			ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(entity);");
			sb.AppendLine("			OriginalValueRecord originalValueRecord = entry.GetUpdatableOriginalValues();");
			sb.AppendLine("			EntityType entityType = context.MetadataWorkspace.GetCSpaceEntityType(entity.GetType());");
			sb.AppendLine();
			sb.AppendLine("			// walk through each property and see if we have an original value for it");
			sb.AppendLine("			// set it if we do.  Walk down through ComplexType properties to set original values");
			sb.AppendLine("			// for each of them also");
			sb.AppendLine("			//");
			sb.AppendLine("			// it is expected that the original values will be sparse because we are trying");
			sb.AppendLine("			// to only capture originals for the ones we are required to have (concurency, sproc, condition, more?)");
			sb.AppendLine("			foreach (EdmProperty property in entityType.Properties)");
			sb.AppendLine("			{");
			sb.AppendLine("				object value;");
			sb.AppendLine("				if (property.TypeUsage.EdmType is PrimitiveType && entity.ChangeTracker.OriginalValues.TryGetValue(property.Name, out value))");
			sb.AppendLine("				{");
			sb.AppendLine("					originalValueRecord.SetValue(property, value);");
			sb.AppendLine("				}");
			sb.AppendLine("				else if (property.TypeUsage.EdmType is ComplexType)");
			sb.AppendLine("				{");
			sb.AppendLine("					OriginalValueRecord complexOriginalValues = originalValueRecord.GetOriginalValueRecord(property.Name);");
			sb.AppendLine("					UpdateOriginalValues((ComplexType)property.TypeUsage.EdmType, entity.GetType().FullName, property.Name, entity.ChangeTracker.OriginalValues, complexOriginalValues);");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static void UpdateOriginalValues(ComplexType complexType, string entityTypeName, string propertyPathToType, IDictionary<string, object> originalValueSource, OriginalValueRecord complexOriginalValueRecord)");
			sb.AppendLine("		{");
			sb.AppendLine("			// Note that complexOriginalValueRecord may be null");
			sb.AppendLine("			// a null complexOriginalValueRecord will only occur if a null reference is assigned");
			sb.AppendLine("			// to a ComplexType property and then given to ApplyChanges.");
			sb.AppendLine("			//");
			sb.AppendLine("			// walk through each property and see if we have an original value for it");
			sb.AppendLine("			// set it if we do.  Walk down through ComplexType properties to set original values");
			sb.AppendLine("			// for each of them also");
			sb.AppendLine("			foreach (EdmProperty property in complexType.Properties)");
			sb.AppendLine("			{");
			sb.AppendLine("				object value;");
			sb.AppendLine("				string propertyPath = String.Format(CultureInfo.InvariantCulture, \"{0}.{1}\", propertyPathToType, property.Name);");
			sb.AppendLine("				if (property.TypeUsage.EdmType is PrimitiveType && originalValueSource.TryGetValue(propertyPath, out value))");
			sb.AppendLine("				{");
			sb.AppendLine("					if (complexOriginalValueRecord != null)");
			sb.AppendLine("					{");
			sb.AppendLine("						complexOriginalValueRecord.SetValue(property, value);");
			sb.AppendLine("					}");
			sb.AppendLine("					else if (value != null)");
			sb.AppendLine("					{");
			sb.AppendLine("						Debug.Assert(complexOriginalValueRecord == null, \"we only throw when the value is not null and the recored is null\");");
			sb.AppendLine("						throw new InvalidOperationException(");
			sb.AppendLine("								String.Format(");
			sb.AppendLine("								CultureInfo.CurrentCulture,");
			sb.AppendLine("								\"Can not set the original value on the object stored in the property '{0}' on the type '{1}' because the property is null.\",");
			sb.AppendLine("								propertyPathToType,");
			sb.AppendLine("								entityTypeName));");
			sb.AppendLine("					}");
			sb.AppendLine("				}");
			sb.AppendLine("				else if (property.TypeUsage.EdmType is ComplexType)");
			sb.AppendLine("				{");
			sb.AppendLine("					OriginalValueRecord nestedOriginalValueRecord = null;");
			sb.AppendLine("					if (complexOriginalValueRecord != null)");
			sb.AppendLine("					{");
			sb.AppendLine("						nestedOriginalValueRecord = complexOriginalValueRecord.GetOriginalValueRecord(property.Name);");
			sb.AppendLine("					}");
			sb.AppendLine("					// recurse down the chain of complex types...");
			sb.AppendLine("					UpdateOriginalValues((ComplexType)property.TypeUsage.EdmType, entityTypeName, propertyPath, originalValueSource, nestedOriginalValueRecord);");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static OriginalValueRecord GetOriginalValueRecord(this OriginalValueRecord record, string name)");
			sb.AppendLine("		{");
			sb.AppendLine("			int ordinal = record.GetOrdinal(name);");
			sb.AppendLine("			if (!record.IsDBNull(ordinal))");
			sb.AppendLine("			{");
			sb.AppendLine("				return record.GetDataRecord(ordinal) as OriginalValueRecord;");
			sb.AppendLine("			}");
			sb.AppendLine("			else");
			sb.AppendLine("			{");
			sb.AppendLine("				return null;");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static void SetValue(this OriginalValueRecord record, EdmProperty edmProperty, object value)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (value == null)");
			sb.AppendLine("			{");
			sb.AppendLine("				Type entityClrType = ((PrimitiveType)edmProperty.TypeUsage.EdmType).ClrEquivalentType;");
			sb.AppendLine("				if (entityClrType.IsValueType &&");
			sb.AppendLine("						!(entityClrType.IsGenericType && typeof(Nullable<>) == entityClrType.GetGenericTypeDefinition()))");
			sb.AppendLine("				{");
			sb.AppendLine("					// Skip setting null original values on non-nullable CLR types because the ObjectStateEntry won't allow this");
			sb.AppendLine("					return;");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			int ordinal = record.GetOrdinal(edmProperty.Name);");
			sb.AppendLine("			record.SetValue(ordinal, value);");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine();
			sb.AppendLine("		private static void ChangeEntityStateBasedOnObjectState(ObjectContext context, " + this.GetClientNamespace() + ".IObjectWithChangeTracker entity)");
			sb.AppendLine("		{");
			sb.AppendLine("			switch (entity.ChangeTracker.State)");
			sb.AppendLine("			{");
			sb.AppendLine("				case (" + this.GetClientNamespace() + ".ObjectState.Added):");
			sb.AppendLine("					// No-op: the state entry is already marked as added");
			sb.AppendLine("					Debug.Assert(context.ObjectStateManager.GetObjectStateEntry(entity).State == EntityState.Added, \"State should have been Added\");");
			sb.AppendLine("					break;");
			sb.AppendLine("				case (" + this.GetClientNamespace() + ".ObjectState.Unchanged):");
			sb.AppendLine("					context.ObjectStateManager.ChangeObjectState(entity, EntityState.Unchanged);");
			sb.AppendLine("					break;");
			sb.AppendLine("				case (" + this.GetClientNamespace() + ".ObjectState.Modified):");
			sb.AppendLine("					context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);");
			sb.AppendLine("					break;");
			sb.AppendLine("				case (" + this.GetClientNamespace() + ".ObjectState.Deleted):");
			sb.AppendLine("					context.ObjectStateManager.ChangeObjectState(entity, EntityState.Deleted);");
			sb.AppendLine("					break;");
			sb.AppendLine();
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static EntityType GetCSpaceEntityType(this MetadataWorkspace workspace, Type type)");
			sb.AppendLine("		{");
			sb.AppendLine("			EntityType ospaceEntityType = null;");
			sb.AppendLine("			StructuralType cspaceEntityType = null;");
			sb.AppendLine("			EntityType entityType = null;");
			sb.AppendLine("			if (workspace.TryGetItem<EntityType>(");
			sb.AppendLine("					type.FullName,");
			sb.AppendLine("					DataSpace.OSpace,");
			sb.AppendLine("					out ospaceEntityType))");
			sb.AppendLine("			{");
			sb.AppendLine("				if (workspace.TryGetEdmSpaceType(");
			sb.AppendLine("						ospaceEntityType,");
			sb.AppendLine("						out cspaceEntityType))");
			sb.AppendLine("				{");
			sb.AppendLine("					entityType = cspaceEntityType as EntityType;");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("			if (entityType == null)");
			sb.AppendLine("			{");
			sb.AppendLine("				throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, \"Unable to find a CSpace type for type {0}\", type.FullName));");
			sb.AppendLine("			}");
			sb.AppendLine("			return entityType;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static object GetValue(this System.Data.Objects.DataClasses.EntityReference entityReference)");
			sb.AppendLine("		{");
			sb.AppendLine("			foreach (object value in entityReference)");
			sb.AppendLine("			{");
			sb.AppendLine("				return value;");
			sb.AppendLine("			}");
			sb.AppendLine("			return null;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static EntityKey GetCurrentEntityKey(this System.Data.Objects.DataClasses.EntityReference entityReference, ObjectContext context)");
			sb.AppendLine("		{");
			sb.AppendLine("			EntityKey currentKey = null;");
			sb.AppendLine("			object currentValue = entityReference.GetValue();");
			sb.AppendLine("			if (currentValue != null)");
			sb.AppendLine("			{");
			sb.AppendLine("				ObjectStateEntry relatedEntry = context.ObjectStateManager.GetObjectStateEntry(currentValue);");
			sb.AppendLine("				currentKey = relatedEntry.EntityKey;");
			sb.AppendLine("			}");
			sb.AppendLine("			else");
			sb.AppendLine("			{");
			sb.AppendLine("				currentKey = entityReference.EntityKey;");
			sb.AppendLine("			}");
			sb.AppendLine("			return currentKey;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static RelatedEnd GetRelatedEnd(this ObjectStateEntry entry, string navigationPropertyIdentity)");
			sb.AppendLine("		{");
			sb.AppendLine("			NavigationProperty navigationProperty =");
			sb.AppendLine("											GetNavigationProperty(entry.ObjectStateManager.MetadataWorkspace.GetCSpaceEntityType(entry.Entity.GetType()), navigationPropertyIdentity);");
			sb.AppendLine("			return entry.RelationshipManager.GetRelatedEnd(");
			sb.AppendLine("					navigationProperty.RelationshipType.FullName, navigationProperty.ToEndMember.Name) as RelatedEnd;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static NavigationProperty GetNavigationProperty(this EntityType entityType, string navigationPropertyIdentity)");
			sb.AppendLine("		{");
			sb.AppendLine("			NavigationProperty navigationProperty;");
			sb.AppendLine("			if (!entityType.NavigationProperties.TryGetValue(navigationPropertyIdentity, false, out navigationProperty))");
			sb.AppendLine("			{");
			sb.AppendLine("				throw new InvalidOperationException(");
			sb.AppendLine("						String.Format(");
			sb.AppendLine("								CultureInfo.CurrentCulture,");
			sb.AppendLine("								\"Could not find navigation property '{0}' in EntityType '{1}'.\",");
			sb.AppendLine("								navigationPropertyIdentity,");
			sb.AppendLine("								entityType.FullName));");
			sb.AppendLine("			}");
			sb.AppendLine("			return navigationProperty;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static string GetEntitySetName(this RelatedEnd relatedEnd)");
			sb.AppendLine("		{");
			sb.AppendLine("			EntitySet entitySet = ((AssociationSet)relatedEnd.RelationshipSet).AssociationSetEnds[relatedEnd.TargetRoleName].EntitySet;");
			sb.AppendLine("			return entitySet.EntityContainer.Name + \".\" + entitySet.Name;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static bool IsDependentEndOfReferentialConstraint(this RelatedEnd relatedEnd)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (null != relatedEnd.RelationshipSet)");
			sb.AppendLine("			{");
			sb.AppendLine("				// NOTE Referential constraints collection will usually contains 0 or 1 element,");
			sb.AppendLine("				// so performance shouldn't be an issue here");
			sb.AppendLine("				foreach (ReferentialConstraint constraint in ((AssociationType)relatedEnd.RelationshipSet.ElementType).ReferentialConstraints)");
			sb.AppendLine("				{");
			sb.AppendLine("					if (constraint.ToRole.Name == relatedEnd.SourceRoleName)");
			sb.AppendLine("					{");
			sb.AppendLine("						// Example:");
			sb.AppendLine("						//    Client<C_ID> --- Order<O_ID, Client_ID>");
			sb.AppendLine("						//    RI Constraint: Principal/From <Client.C_ID>,  Dependent/To <Order.Client_ID>");
			sb.AppendLine("						// When current RelatedEnd is a CollectionOrReference in Order's relationships,");
			sb.AppendLine("						// constarint.ToRole == this._fromEndProperty == Order");
			sb.AppendLine("						return true;");
			sb.AppendLine("					}");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("			return false;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private static bool TryGetObjectStateEntry(this ObjectContext context, EntityKey from, EntityKey to, AssociationSet associationSet, AssociationEndMember fromEnd, AssociationEndMember toEnd, out ObjectStateEntry entry)");
			sb.AppendLine("		{");
			sb.AppendLine("			entry = null;");
			sb.AppendLine("			foreach (var relationshipEntry in (from e in context.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Unchanged)");
			sb.AppendLine("																				 where e.IsRelationship && e.EntitySet == associationSet");
			sb.AppendLine("																				 select e))");
			sb.AppendLine("			{");
			sb.AppendLine("				CurrentValueRecord currentValues = relationshipEntry.CurrentValues;");
			sb.AppendLine("				int fromOrdinal = currentValues.GetOrdinal(fromEnd.Name);");
			sb.AppendLine("				int toOrdinal = currentValues.GetOrdinal(toEnd.Name);");
			sb.AppendLine("				if (((EntityKey)currentValues.GetValue(fromOrdinal)) == from &&");
			sb.AppendLine("						((EntityKey)currentValues.GetValue(toOrdinal)) == to)");
			sb.AppendLine("				{");
			sb.AppendLine("					entry = relationshipEntry;");
			sb.AppendLine("					return true;");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("			return false;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private sealed class AddHelper");
			sb.AppendLine("		{");
			sb.AppendLine("			private readonly ObjectContext _context;");
			sb.AppendLine("			private readonly EntityIndex _entityIndex;");
			sb.AppendLine();
			sb.AppendLine("			// Used during add processing");
			sb.AppendLine("			private readonly Queue<Tuple<string, " + this.GetClientNamespace() + ".IObjectWithChangeTracker>> _entitiesToAdd;");
			sb.AppendLine("			private readonly Queue<Tuple<ObjectStateEntry, string, IEnumerable<object>>> _entitiesDuringAdd;");
			sb.AppendLine();
			sb.AppendLine("			public static EntityIndex AddAllEntities(ObjectContext context, string entitySetName, " + this.GetClientNamespace() + ".IObjectWithChangeTracker entity)");
			sb.AppendLine("			{");
			sb.AppendLine("				AddHelper addHelper = new AddHelper(context);");
			sb.AppendLine();
			sb.AppendLine("				try");
			sb.AppendLine("				{");
			sb.AppendLine("					// Include the root element to start the Apply");
			sb.AppendLine("					addHelper.QueueAdd(entitySetName, entity);");
			sb.AppendLine();
			sb.AppendLine("					// Add everything");
			sb.AppendLine("					while (addHelper.HasMore)");
			sb.AppendLine("					{");
			sb.AppendLine("						Tuple<string, " + this.GetClientNamespace() + ".IObjectWithChangeTracker> entityInSet = addHelper.NextAdd();");
			sb.AppendLine("						// Only add the object if it's not already in the context");
			sb.AppendLine("						ObjectStateEntry entry = null;");
			sb.AppendLine("						if (!context.ObjectStateManager.TryGetObjectStateEntry(entityInSet.Item2, out entry))");
			sb.AppendLine("						{");
			sb.AppendLine("							context.AddObject(entityInSet.Item1, entityInSet.Item2);");
			sb.AppendLine("						}");
			sb.AppendLine("					}");
			sb.AppendLine("				}");
			sb.AppendLine("				finally");
			sb.AppendLine("				{");
			sb.AppendLine("					addHelper.Detach();");
			sb.AppendLine("				}");
			sb.AppendLine("				return addHelper.EntityIndex;");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			private AddHelper(ObjectContext context)");
			sb.AppendLine("			{");
			sb.AppendLine("				_context = context;");
			sb.AppendLine("				_context.ObjectStateManager.ObjectStateManagerChanged += this.HandleStateManagerChange;");
			sb.AppendLine();
			sb.AppendLine("				_entityIndex = new EntityIndex(context);");
			sb.AppendLine("				_entitiesToAdd = new Queue<Tuple<string, " + this.GetClientNamespace() + ".IObjectWithChangeTracker>>();");
			sb.AppendLine("				_entitiesDuringAdd = new Queue<Tuple<ObjectStateEntry, string, IEnumerable<object>>>();");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			private void Detach()");
			sb.AppendLine("			{");
			sb.AppendLine("				_context.ObjectStateManager.ObjectStateManagerChanged -= this.HandleStateManagerChange;");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			private void HandleStateManagerChange(object sender, CollectionChangeEventArgs args)");
			sb.AppendLine("			{");
			sb.AppendLine("				if (args.Action == CollectionChangeAction.Add)");
			sb.AppendLine("				{");
			sb.AppendLine("					" + this.GetClientNamespace() + ".IObjectWithChangeTracker entity = args.Element as " + this.GetClientNamespace() + ".IObjectWithChangeTracker;");
			sb.AppendLine("					ObjectStateEntry entry = _context.ObjectStateManager.GetObjectStateEntry(entity);");
			sb.AppendLine("					" + this.GetClientNamespace() + ".ObjectChangeTracker changeTracker = entity.ChangeTracker;");
			sb.AppendLine();
			sb.AppendLine("					changeTracker.ChangeTrackingEnabled = false;");
			sb.AppendLine("					_entityIndex.Add(entry, changeTracker);");
			sb.AppendLine();
			sb.AppendLine("					// Queue removed reference values");
			sb.AppendLine("					var navPropNames = _context.MetadataWorkspace.GetCSpaceEntityType(entity.GetType()).NavigationProperties.Select(n => n.Name);");
			sb.AppendLine("					var entityRefOriginalValues = changeTracker.OriginalValues.Where(kvp => navPropNames.Contains(kvp.Key));");
			sb.AppendLine("					foreach (KeyValuePair<string, object> originalValueWithName in entityRefOriginalValues)");
			sb.AppendLine("					{");
			sb.AppendLine("						if (originalValueWithName.Value != null)");
			sb.AppendLine("						{");
			sb.AppendLine("							_entitiesDuringAdd.Enqueue(new Tuple<ObjectStateEntry, string, IEnumerable<object>>(");
			sb.AppendLine("									entry,");
			sb.AppendLine("									originalValueWithName.Key,");
			sb.AppendLine("									new object[] { originalValueWithName.Value }));");
			sb.AppendLine("						}");
			sb.AppendLine("					}");
			sb.AppendLine();
			sb.AppendLine("					// Queue removed collection values");
			sb.AppendLine("					foreach (KeyValuePair<string, " + this.GetClientNamespace() + ".ObjectList> collectionPropertyChangesWithName in changeTracker.ObjectsRemovedFromCollectionProperties)");
			sb.AppendLine("					{");
			sb.AppendLine("						_entitiesDuringAdd.Enqueue(new Tuple<ObjectStateEntry, string, IEnumerable<object>>(");
			sb.AppendLine("								entry,");
			sb.AppendLine("								collectionPropertyChangesWithName.Key,");
			sb.AppendLine("								collectionPropertyChangesWithName.Value));");
			sb.AppendLine("					}");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			private EntityIndex EntityIndex");
			sb.AppendLine("			{");
			sb.AppendLine("				get { return _entityIndex; }");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			private bool HasMore");
			sb.AppendLine("			{");
			sb.AppendLine("				get { ProcessNewAdds(); return _entitiesToAdd.Count > 0; }");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			private void QueueAdd(string entitySetName, " + this.GetClientNamespace() + ".IObjectWithChangeTracker entity)");
			sb.AppendLine("			{");
			sb.AppendLine("				if (!_entityIndex.Contains(entity))");
			sb.AppendLine("				{");
			sb.AppendLine("					// Queue the entity so that we can add the 'removed collection' items");
			sb.AppendLine("					_entitiesToAdd.Enqueue(new Tuple<string, " + this.GetClientNamespace() + ".IObjectWithChangeTracker>(entitySetName, entity));");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			private Tuple<string, " + this.GetClientNamespace() + ".IObjectWithChangeTracker> NextAdd()");
			sb.AppendLine("			{");
			sb.AppendLine("				ProcessNewAdds();");
			sb.AppendLine("				return _entitiesToAdd.Dequeue();");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			private void ProcessNewAdds()");
			sb.AppendLine("			{");
			sb.AppendLine("				while (_entitiesDuringAdd.Count > 0)");
			sb.AppendLine("				{");
			sb.AppendLine("					Tuple<ObjectStateEntry, string, IEnumerable<object>> relatedEntities = _entitiesDuringAdd.Dequeue();");
			sb.AppendLine("					RelatedEnd relatedEnd = relatedEntities.Item1.GetRelatedEnd(relatedEntities.Item2);");
			sb.AppendLine("					string entitySetName = relatedEnd.GetEntitySetName();");
			sb.AppendLine();
			sb.AppendLine("					foreach (var targetEntity in relatedEntities.Item3)");
			sb.AppendLine("					{");
			sb.AppendLine("						QueueAdd(entitySetName, targetEntity as " + this.GetClientNamespace() + ".IObjectWithChangeTracker);");
			sb.AppendLine("					}");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private sealed class EntityIndex");
			sb.AppendLine("		{");
			sb.AppendLine("			private readonly ObjectContext _context;");
			sb.AppendLine();
			sb.AppendLine("			// Set of all entities");
			sb.AppendLine("			private readonly HashSet<" + this.GetClientNamespace() + ".IObjectWithChangeTracker> _allEntities;");
			sb.AppendLine();
			sb.AppendLine("			// Index of the final key that will be used in the context (could be real for non-added, could be temporary for added)");
			sb.AppendLine("			// to the initial temporary key");
			sb.AppendLine("			private readonly Dictionary<EntityKey, EntityKey> _temporaryKeyMap;");
			sb.AppendLine();
			sb.AppendLine("			public EntityIndex(ObjectContext context)");
			sb.AppendLine("			{");
			sb.AppendLine("				_context = context;");
			sb.AppendLine();
			sb.AppendLine("				_allEntities = new HashSet<" + this.GetClientNamespace() + ".IObjectWithChangeTracker>();");
			sb.AppendLine("				_temporaryKeyMap = new Dictionary<EntityKey, EntityKey>();");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			public void Add(ObjectStateEntry entry, " + this.GetClientNamespace() + ".ObjectChangeTracker changeTracker)");
			sb.AppendLine("			{");
			sb.AppendLine("				EntityKey temporaryKey = entry.EntityKey;");
			sb.AppendLine("				EntityKey finalKey;");
			sb.AppendLine();
			sb.AppendLine("				if (!_allEntities.Contains(entry.Entity))");
			sb.AppendLine("				{");
			sb.AppendLine("					// Track that this Apply will be handling this entity");
			sb.AppendLine("					_allEntities.Add(entry.Entity as " + this.GetClientNamespace() + ".IObjectWithChangeTracker);");
			sb.AppendLine("				}");
			sb.AppendLine();
			sb.AppendLine("				if (changeTracker.State == " + this.GetClientNamespace() + ".ObjectState.Added)");
			sb.AppendLine("				{");
			sb.AppendLine("					finalKey = temporaryKey;");
			sb.AppendLine("				}");
			sb.AppendLine("				else");
			sb.AppendLine("				{");
			sb.AppendLine("					finalKey = _context.CreateEntityKey(temporaryKey.EntityContainerName + \".\" + temporaryKey.EntitySetName, entry.Entity);");
			sb.AppendLine("				}");
			sb.AppendLine("				if (!_temporaryKeyMap.ContainsKey(finalKey))");
			sb.AppendLine("				{");
			sb.AppendLine("					_temporaryKeyMap.Add(finalKey, temporaryKey);");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			public bool Contains(object entity)");
			sb.AppendLine("			{");
			sb.AppendLine("				return _allEntities.Contains(entity);");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			public IEnumerable<" + this.GetClientNamespace() + ".IObjectWithChangeTracker> AllEntities");
			sb.AppendLine("			{");
			sb.AppendLine("				get { return _allEntities; }");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			// Converts the passed in EntityKey to the EntityKey that is usable by the current state of ApplyChanges");
			sb.AppendLine("			public EntityKey ConvertEntityKey(EntityKey targetKey)");
			sb.AppendLine("			{");
			sb.AppendLine("				ObjectStateEntry targetEntry;");
			sb.AppendLine("				if (!_context.ObjectStateManager.TryGetObjectStateEntry(targetKey, out targetEntry))");
			sb.AppendLine("				{");
			sb.AppendLine("					// If no entry exists, then either:");
			sb.AppendLine("					// 1. This is an EntityKey that is not represented in the set of entities being dealt with during the Apply");
			sb.AppendLine("					// 2. This is an EntityKey that will represent one of the yet-to-be-processed Added entries, so look it up");
			sb.AppendLine("					EntityKey temporaryKey;");
			sb.AppendLine("					if (_temporaryKeyMap.TryGetValue(targetKey, out temporaryKey))");
			sb.AppendLine("					{");
			sb.AppendLine("						targetKey = temporaryKey;");
			sb.AppendLine("					}");
			sb.AppendLine("				}");
			sb.AppendLine("				return targetKey;");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		// The RelationshipSet builds a list of all relationships from an");
			sb.AppendLine("		// initial set of entities");
			sb.AppendLine("		private sealed class RelationshipSet : IEnumerable<RelationshipWrapper>");
			sb.AppendLine("		{");
			sb.AppendLine("			private readonly HashSet<RelationshipWrapper> _relationships;");
			sb.AppendLine("			private readonly ObjectContext _context;");
			sb.AppendLine();
			sb.AppendLine("			public RelationshipSet(ObjectContext context, IEnumerable<object> allEntities)");
			sb.AppendLine("			{");
			sb.AppendLine("				_context = context;");
			sb.AppendLine("				_relationships = new HashSet<RelationshipWrapper>();");
			sb.AppendLine("				foreach (object entity in allEntities)");
			sb.AppendLine("				{");
			sb.AppendLine("					ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(entity);");
			sb.AppendLine("					foreach (IRelatedEnd relatedEnd in entry.RelationshipManager.GetAllRelatedEnds())");
			sb.AppendLine("					{");
			sb.AppendLine("						if (!((AssociationType)relatedEnd.RelationshipSet.ElementType).IsForeignKey)");
			sb.AppendLine("						{");
			sb.AppendLine("							foreach (object targetEntity in relatedEnd)");
			sb.AppendLine("							{");
			sb.AppendLine("								Add(relatedEnd, entity, targetEntity, EntityState.Unchanged);");
			sb.AppendLine("							}");
			sb.AppendLine("						}");
			sb.AppendLine("					}");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			// Adds an entry to the index based on a IRelatedEnd");
			sb.AppendLine("			public void Add(IRelatedEnd relatedEnd, object sourceEntity, object targetEntity, EntityState state)");
			sb.AppendLine("			{");
			sb.AppendLine("				RelationshipWrapper wrapper = new RelationshipWrapper(");
			sb.AppendLine("														(AssociationSet)relatedEnd.RelationshipSet,");
			sb.AppendLine("														relatedEnd.SourceRoleName,");
			sb.AppendLine("														sourceEntity,");
			sb.AppendLine("														relatedEnd.TargetRoleName,");
			sb.AppendLine("														targetEntity,");
			sb.AppendLine("														state);");
			sb.AppendLine("				if (!_relationships.Contains(wrapper))");
			sb.AppendLine("				{");
			sb.AppendLine("					_relationships.Add(wrapper);");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			// Removes an entry from the index based on a relationship ObjectStateEntry");
			sb.AppendLine("			public void Remove(ObjectStateEntry relationshipEntry)");
			sb.AppendLine("			{");
			sb.AppendLine("				Debug.Assert(relationshipEntry.IsRelationship);");
			sb.AppendLine("				AssociationSet associationSet = (AssociationSet)relationshipEntry.EntitySet;");
			sb.AppendLine("				DbDataRecord values = relationshipEntry.State == EntityState.Deleted ? relationshipEntry.OriginalValues : relationshipEntry.CurrentValues;");
			sb.AppendLine("				int fromOridinal = values.GetOrdinal(associationSet.ElementType.AssociationEndMembers[0].Name);");
			sb.AppendLine("				object fromEntity = _context.ObjectStateManager.GetObjectStateEntry((EntityKey)values.GetValue(fromOridinal)).Entity;");
			sb.AppendLine("				int toOridinal = values.GetOrdinal(associationSet.ElementType.AssociationEndMembers[1].Name);");
			sb.AppendLine("				object toEntity = _context.ObjectStateManager.GetObjectStateEntry((EntityKey)values.GetValue(toOridinal)).Entity;");
			sb.AppendLine();
			sb.AppendLine("				if (fromEntity != null && toEntity != null)");
			sb.AppendLine("				{");
			sb.AppendLine("					RelationshipWrapper wrapper = new RelationshipWrapper(");
			sb.AppendLine("							associationSet,");
			sb.AppendLine("							associationSet.ElementType.AssociationEndMembers[0].Name,");
			sb.AppendLine("							fromEntity,");
			sb.AppendLine("							associationSet.ElementType.AssociationEndMembers[1].Name,");
			sb.AppendLine("							toEntity,");
			sb.AppendLine("							EntityState.Unchanged);");
			sb.AppendLine();
			sb.AppendLine("					_relationships.Remove(wrapper);");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			#region IEnumerable<RelationshipWrapper>");
			sb.AppendLine();
			sb.AppendLine("			public IEnumerator<RelationshipWrapper> GetEnumerator()");
			sb.AppendLine("			{");
			sb.AppendLine("				return _relationships.GetEnumerator();");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()");
			sb.AppendLine("			{");
			sb.AppendLine("				return _relationships.GetEnumerator();");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			#endregion");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		// A RelationshipWrapper is used to identify a relationship between two entities");
			sb.AppendLine("		// The relationship is identified by the AssociationSet, and the order of the entities based");
			sb.AppendLine("		// on the roles they play (via AssociationEndMember)");
			sb.AppendLine("		private sealed class RelationshipWrapper : IEquatable<RelationshipWrapper>");
			sb.AppendLine("		{");
			sb.AppendLine("			internal readonly AssociationSet AssociationSet;");
			sb.AppendLine("			internal readonly object End0;");
			sb.AppendLine("			internal readonly object End1;");
			sb.AppendLine("			internal readonly EntityState State;");
			sb.AppendLine();
			sb.AppendLine("			internal RelationshipWrapper(AssociationSet extent,");
			sb.AppendLine("																	 string role0, object end0,");
			sb.AppendLine("																	 string role1, object end1,");
			sb.AppendLine("																	 EntityState state)");
			sb.AppendLine("			{");
			sb.AppendLine("				Debug.Assert(null != extent, \"null AssociationSet\");");
			sb.AppendLine("				Debug.Assert(null != (object)end0, \"null end0\");");
			sb.AppendLine("				Debug.Assert(null != (object)end1, \"null end1\");");
			sb.AppendLine();
			sb.AppendLine("				AssociationSet = extent;");
			sb.AppendLine("				Debug.Assert(extent.ElementType.AssociationEndMembers.Count == 2, \"only 2 ends are supported\");");
			sb.AppendLine();
			sb.AppendLine("				State = state;");
			sb.AppendLine();
			sb.AppendLine("				if (extent.ElementType.AssociationEndMembers[0].Name == role0)");
			sb.AppendLine("				{");
			sb.AppendLine("					Debug.Assert(extent.ElementType.AssociationEndMembers[1].Name == role1, \"a)roleAndKey1 Name differs\");");
			sb.AppendLine("					End0 = end0;");
			sb.AppendLine("					End1 = end1;");
			sb.AppendLine("				}");
			sb.AppendLine("				else");
			sb.AppendLine("				{");
			sb.AppendLine("					Debug.Assert(extent.ElementType.AssociationEndMembers[0].Name == role1, \"b)roleAndKey1 Name differs\");");
			sb.AppendLine("					Debug.Assert(extent.ElementType.AssociationEndMembers[1].Name == role0, \"b)roleAndKey0 Name differs\");");
			sb.AppendLine("					End0 = end1;");
			sb.AppendLine("					End1 = end0;");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			internal ReadOnlyMetadataCollection<AssociationEndMember> AssociationEndMembers");
			sb.AppendLine("			{");
			sb.AppendLine("				get { return this.AssociationSet.ElementType.AssociationEndMembers; }");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			public override int GetHashCode()");
			sb.AppendLine("			{");
			sb.AppendLine("				return this.AssociationSet.Name.GetHashCode() ^ (this.End0.GetHashCode() + this.End1.GetHashCode());");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			public override bool Equals(object obj)");
			sb.AppendLine("			{");
			sb.AppendLine("				return Equals(obj as RelationshipWrapper);");
			sb.AppendLine("			}");
			sb.AppendLine();
			sb.AppendLine("			public bool Equals(RelationshipWrapper wrapper)");
			sb.AppendLine("			{");
			sb.AppendLine("				return (Object.ReferenceEquals(this, wrapper) ||");
			sb.AppendLine("								((null != wrapper) &&");
			sb.AppendLine("								 Object.ReferenceEquals(this.AssociationSet, wrapper.AssociationSet) &&");
			sb.AppendLine("								 Object.ReferenceEquals(this.End0, wrapper.End0) &&");
			sb.AppendLine("								 Object.ReferenceEquals(this.End1, wrapper.End1)));");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine("	}");
			sb.AppendLine();
		}

		#endregion

	}
}